package demo07String;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User:咭咭熊
 * Date:2021 - 09 - 27
 * Time:16:15
 */

import java.lang.reflect.Field;
import java.util.Arrays;

/***
 * 1. String 的学习——定义的3种方式
 *  String 类的两种实例化操作：
 * （1）直接赋值
 *
 *   1）如果采用直接赋值的方式进行String类的对象实例化操作，那么该实例对象（字符串内容）将自欧东烦恼歌保存到这个对象池中；
 *  2）如果下次继续使用直接赋值的方式声明String类对象，此时对象池中若存在指定字符串内容，则直接进行引用；
 *  3）如果没有，则开辟新的字符析串对象后将其保存在对象池中以供下一次继续使用
 * （2）new一个新的字符串
 *   构造方法：会开辟两块堆内存空间。其中一块称为垃圾空间，不会自动保存到对象池中，可以使用intern()方法手工入池。
 *
 * 2. String属于引用类型；
 *
 * 3. String类是final修饰的；
 *    public final char value[]; value[]数组也是final进行修饰的，
 *    String所指向的对象内容是不可以进行修改的；
 *
 *  4. == 比较引用地址是否相同；
 *
 *  5. equals()比较字符串内容是否相同
 *
 *  6.字符串是一种不可变对象，它的内容不可以改变；
 *  String的内部实现是基于final修饰的char[]数组来实现的，但是String类没有提供set方法之类的来修改内部的字符数组；
 *
 * 7.解释String \StringBUilder\StringBUffer的区别：
 * String是不可变字符串【内容不可以修改】，StrinBuilder和StringBuffer的内容可以修改；
 * StringBUilder 和StringBUffer的大部分功能是相似的；
 * StringBuilder 采用异步处理，属于线程不安全操作；——单线程
 * StringBuffer 采用同步处理synchronized，属于线程安全的操作；——多线程
 *
 *
 */
public class TestString {
    /***
     * 26.String的for循环拼接
     *  在for循环当中不可以使用String直接进行拼接，这样会产生大量的临时对象
     * @param args
     */
    public static void main(String[] args) {
/*        String str = "abc";
        for (int i = 0; i < 10; i++) {
            str += i;
        }
        System.out.println(str);//abc0123456789每次拼接会产生大量的临时对象*/


/*        String str = "abc";
        StringBuilder sb = new StringBuilder();
        sb.append(str);
        for (int i = 0; i < 10; i++) {
            sb.append(i);
        }
        str = sb.toString();
        System.out.println(str);//abc0123456789*/

        String str = "abc";
        for (int i = 0; i < 10; i++) {
            StringBuilder sb = new StringBuilder();
            str = sb.append(str).append(i).toString();//str += i;
        }
        System.out.println(str);//abc0123456789
    }
    /***
     * 25.String 的优化过程：会被优化为StringBuilder
     * @param args
     */
    public static void main25(String[] args) {
/*        StringBuilder sb = new StringBuilder();
        sb.append("abcdef");
        sb.append("hello");
        String str = sb.toString();
        System.out.println(str);*/

        //定义的String str被优化为StringBuilder的过程
        String str = "abcdef";
        StringBuilder sb = new StringBuilder();

        sb.append(str);
        sb.append("hello");
        str = sb.toString();
        System.out.println(str);

    }
    /***
     * 24.三种字符串：String StringBuilder StringBuffer
     *
     *    共同点：都可以表示字符串
     *    三者的区别：
     *      a.后两者包含了一些String 没有的方法；如reverse
     *      b.后两者是可变的，String是不可变的；
     *          String的每次拼接都会产生新的对象；
     *          后两者每次的而拼接返回的都是this
     *      c.StringBuilder和StringBUffer的区别：
     *         StringBUilder和String一般出现在单线程情况；
     *          StringBuffer是使用synchronized【加锁：用于保证线程安全】修饰，一般出现在多线程情况下；
     *      d.StringBuilder和String的区别：
     *          String的拼接 + 会被优化；优化为StringBuilder的append方法,在调用toString()方法
     *           在循环当中不可以使用String直接进行拼接，这样会产生大量的临时对象; 包括优化之后的StringBuilder;
     * (1)String
     *
     * (2)StringBuilder
     *      public final class StringBUilder extebds Object implement Serializable ,CharSerquence
     *      一个可变的字符序列；
     *      此类设计用作简易替换StringBuffer正在使用由单个线程字符串缓冲区的地方；
     *      StringBuilder的常见方法：append() insert()
     * (3)StringBuffer
     * @param args
     */
    public static void main24(String[] args) {
        String str = "abcdef";
        str += "hello";
        System.out.println(str);

        StringBuilder sb = new StringBuilder("ABCDE");
        //查看源码发现：append()方法的返回值是retrun this，即返回当前对象
        System.out.println(sb.append("hello"));
        //可以使用StringBuilder的reverse()方法对字符串实现逆置
        System.out.println(sb.reverse());
        //StringBuffer的append反射光hi是使用synchronized进行修饰的：可以保证线程的安全
        StringBuffer sf = new StringBuffer("abcde");
        System.out.println(sf.append("hello"));
    }
    /***
     * 23.
     * (1)trim—-对字符串去掉左右的两边的空格
     * (2)toUpperCase
     * (3)toLowerCase
     * (4)length()
     * (5)isEmpty()--判断是否为空字符串
     * (6)concat(String str)——字符串拼接（等同于+，不入池）
     * （7）intern()——字符串入池操作
     * @param args
     */
    public static void main23(String[] args) {
        String str1 = null;//null代表不指向任何对象
        String str2 = "";//代表指向的对象什么都没有
        System.out.println(str2.isEmpty());
    }
    /***
     * 22.字符串截取
     * subString(int beginIndex,inrt endIndex)——返回原字符串的一个子串
     * @param args
     */
    public static void main22(String[] args) {
        String str = "abcdef";
        //
        System.out.println(str.substring(1,4));
    }
    /***
     * 21.同时以多个分割符对字符串进行分割，需要使用连字符|
     * @param args
     */
    public static void main21(String[] args) {
        String str = "hello30-split#world";
        String[] str1 = str.split("-|#");
        for (int i = 0; i < str1.length; i++) {
            System.out.println(str1[i]);
            //hello30
            //split
            //world
        }
    }
    /***
     * 20.
     * 需要对特殊字符（. *）使用转义字符
     * @param args
     */
    public static void main20(String[] args) {
        String str = "192.168.1.1";
        String[] str1 = str.split("\\.");//此处需要使用转义字符
        for (int i = 0; i < str1.length; i++) {
            System.out.println(str1[i]);
            //192
            //168
            //1
            //1
        }
    }
    /**
     * 19.只拿到zhangsan和123
     * @param args
     */
    public static void main19(String[] args) {
        String  str = "username=zhangsan&password=123";
        String[] str1 = str.split("&");
        for (int i = 0; i < str1.length; i++) {
            System.out.println(str1[i]);
            //username=zhangsan
            //password=123

            //在对其使用=进行切割
            String[] str2 = str1[i].split("=");
            System.out.println(str2[1]);
        }
    }
    /**
     * 18.对地址栏的用户名和密码信息进行分割
     * @param args
     */
    public static void main18(String[] args) {
        String  str = "username=zhangsan&password=123";
        String[] str1 = str.split("&");
        for (int i = 0; i < str1.length; i++) {
            System.out.println(str1[i]);
            //username=zhangsan
            //password=123

            //在对其使用=进行切割
            String[] str2 = str1[i].split("=");
            for (int j = 0; j < str2.length; j++) {
                System.out.println(str2[j]);
            }
        }
    }
    /***
     * 17字符串的拆分：
     * （1）public String[] split(Stirng regex)——将字符串全部拆分
     * （2）public Stirng[] split(String regex,int limit)——将字符串部分拆分，该数组长度就是limit
     * @param args
     */
    public static void main17(String[] args) {
        String str1 = "abcdef";
        String[] ret = str1.split("c");
        for (String s:ret) {
            System.out.println(s);
        }
    }
    /***
     * 16.字符串替换：使用一个指定的新的字符串替换掉已有的字符串数据
     * replaceAll(String regex,String replacement)——替换所有指定的内容
     * replaceFirst(String regex,String replacement)——替换首个内容
     * @param args
     */
    public static void main16(String[] args) {
        String str1 = "abcdefabcdefabcdef";
        //public String replace(CharSequence target,CharSequence replacement){}
        System.out.println(str1.replace("ab","xx"));
        //public final class String implenments Comparable<String>,Charsequence{
        // public final char value[];}
        System.out.println(str1.replaceAll("ab","*"));
        //*cdef*cdef*cdef
        System.out.println(str1.replaceFirst("ab","xx"));
        //xxcdefabcdefabcdef

        //接口可以引用接口的实现类
        CharSequence charSequence = new String();
        Comparable comparable = new String();
    }
    /***
     * 15.字符串的常用方法：
     * （1）indeOf——返回指定字符的下标
     * （2）lastIndexOf——从指定位置由后往前找
     * （3）starttWith（String prefix）——判断是否以指定字符串开头
     * （4）endsWith(String suffix)——判断是否以指定字符串结尾
     * （5）contains——字符串查找
     * @param args
     */
    public static void main15(String[] args) {
        String str1 = "ababcdef";
        System.out.println(str1.contains("bc"));
        System.out.println(str1.indexOf("b"));
        //lastIndexOf：从指定位置由后往前找
        System.out.println(str1.lastIndexOf("ab"));
        System.out.println(str1.startsWith("ab"));
        System.out.println(str1.endsWith("ef"));
    }
    /***
     * 14.比较方式
     * ==
     * equals
     * compareTo
     * @param args
     */
    public static void main14(String[] args) {
        String str1 = "hello";
        String str2 = "world";
        String str3 = "hello" + "world";
        String str4 = str1 + "world";
        String str5 = str1 + str2;
        System.out.println(str3 == str4);//f
        System.out.println(str3 == str5);//f
        System.out.println(str4 == str5);//f
        System.out.println(str3.equals(str4));


        System.out.println(str1.compareTo(str2));//相等返回0，前者大于后者：返回一个大于0的数字
    }
    //实现字符串的逆置
    public static String reverse(String string){
        //先将字符串转换成数组，在完成逆置
        char[] chars = string.toCharArray();
        int left = 0;
        int right = chars.length - 1;
        while(left < right){
            char tmp = chars[left];
            chars[left] = chars[right];
            chars[right] = tmp;
            left++;
            right--;
        }
        //返回值：将数组转换为字符串:3种方式
        /*return new String(chars);*/
        /*return String.copyValueOf(chars);*/
        return String.valueOf(chars);
    }

    public static void main13(String[] args) {
        String str = "abcde";
        System.out.println(reverse(str));//edcba
    }
    /***
     * 12.常用方法：
     * 字符串内部包含一个字符数组，String和char[] 可以相互转换
     * （1）public String(char[] vlaue)——将字符数组中的所有内容转变为字符串
     * （2）public String（char[] value,int offset,int count）——将部分字符数组中的内容变为字符串
     * （3） public char charAt(int index)——取得指定所谓位置的字符，索引从0开始
     * （4）public char[] toCharArray()——将字符串变为字符数组返回
     *
     * @param args
     */
    public static void main12(String[] args) {
        String str1 = "abcde";
        //charAt()——获取指定位置的字符
        System.out.println(str1.charAt(0));//a

        char[] value = {'h','e','l','l','o'};
        //将部分字符数组中的内容转换为字符数组
        String str2 = new String(value,1,2);
        System.out.println(str2);//el

        //将字符串转换为字符数组
        char[] chars = str1.toCharArray();
        System.out.println(Arrays.toString(chars));
    }
    /***
     * 11.使用反射可以岁字符串内容进行修改
     * 反射的应用：Spring
     * 反射操作以破坏封装，访问一个类内部的private成员；
     * 在String类中定义的：
     * private final char value[];
     * @param args
     */
    public static void main11(String[] args) throws NoSuchFieldException, IllegalAccessException {
        String str = "Hello";
        //(1)获取String类中的value字段，将这个value和String源码中的value是匹配的
        Field valueField = String.class.getDeclaredField("value");
        //(2)将这个字段的访问呢属性设置为true
        valueField.setAccessible(true);
        //(3)把str中的value属性获取到
        char[] value = (char[])valueField.get(str);
        //(4)修改value的值
        value[0] = 'h';
        System.out.println(str);//hello
    }
    /***
     * 10.此过程会出现大量的临时变量
     * 引入StringBuilder  StringBufffer
     * @param args
     */
    public static void main10(String[] args) {
        String str1 = "abc";
        for (int i = 0; i <= 10 ; i++) {
            str1 += i;
        }
        System.out.println(str1);//abc012345678910
    }
    /***
     * 9.字符串的内容不可以进行修改；
     * 使用 + 进行拼接并没有修改原来的字符串，
     * 而是创建了新的对象来保存拼接之后的对象
     * @param args
     */
    public static void main9(String[] args) {
        String str1 = "abc";
        System.out.println(str1);//abc
        str1 = str1 + "de";
        System.out.println(str1);//abcde  并没有对str1 进行修改，而是产生的新的对象
        str1 = str1 + "f";
        System.out.println(str1);//abcdef
    }

    /***
     * 8.使用equals方法的时候，当一个字符串为null
     * @param args
     */
    public static void main8(String[] args) {
        String str1 = null;
        String str2 = "abcde";
        //boolean flag1 = str1.equals(str2);//java.lang.NullPointerException
        //System.out.println(flag1);
        boolean flag = str2.equals(str1);
        System.out.println(flag);//false

    }
    /***
     * 7.字符串比较内容相等
     *
     * == 比较引用地址是否相同；
     * equals()比较字符串内容是否相同
     * @param args
     */
    public static void main7(String[] args) {
        String str1 = "hello";
        String str2 = "hello";
        System.out.println(str1 == str2);//== 比较引用地址是否相同
        //equals（） 方法:将字符串转化为数组，比较每个下标对应的内容是否相等
        System.out.println(str1.equals(str2));
    }

    /***
     * 6.字符串常量是不能进行修改的；其底层是由一个value[]数组进行接收的，其是由final进行修饰的
     * @param args
     */
    public static void main6(String[] args) {
        String str1 = "Hello";
        String str2 = str1;
        str1 = "hello";//并没有对str1 进行修改，而是使其指向了新的对象内容“hello"
        System.out.println(str1);//hello
        System.out.println(str2);//Hello
    }
    /***
     * 5.字符串的引用赋值
     * @param args
     */
    public static void main5(String[] args) {
        String str1 = "abc";
        //str2指向了str1指向的对象
        String str2 = str1;
        System.out.println(str1);
        System.out.println(str2);
        System.out.println(str1 == str2);//true
    }
    /***
     * 4.intern()——检查常量池中是否存在“abcde”字符串，如果有，value[]数组就引用该常量池的字符串“abcde”,再将该字符串的引用再给str2
     * @param args
     */
    public static void main4(String[] args) {
        //调用intern()方法之后，str1 和 str2得到的是相同的引用
        String str1 = "abcde";
        String str2 = new String("abcde").intern();
        System.out.println(str1 == str2);//true
    }
    /***
     * 3.字符串的比较
     * @param args
     */
    public static void main3(String[] args) {
        String str1= "abcde";//"abcde"——直接存放于常量池中
        String str2 = new String("abcde");
        System.out.println(str1 == str2);//f

        //"abc"+ "de" 在编译的时候就进行了拼接 即“abcde”
        String str3 = "abc"+ "de";
        System.out.println(str1 == str3);//t

        String str4 = new String("abc") + new String("de");//new存放于堆上
        System.out.println(str1 == str4);//f

        String str5 = "abc" + new String("de");
        System.out.println(str1 == str5);//f

        String str6 = new String("abc") + "de";
        System.out.println(str1 == str6);//f

        System.out.println(str4 == str5);// false

        System.out.println(str5 == str6);//false
    }
    /***
     * 2.字符串的比较
     * @param args
     */
    public static void main2(String[] args) {
        String str1 = "Hello";
        String str2 = new String("Hello");
        char[] value = {'H','e','l','l','o'};
        String str3 = new String(value);

        //== 两边比较的是值。
        // str1 str2  str3 此时都是引用  变量中保存的都是地址
        System.out.println(str1 == str2);//false
        System.out.println(str1 == str3);//false
        System.out.println(str2 == str3);//fasle
    }
    /***
     * 1.java中构建字符串的三种方式
     * @param args
     */
    public static void main1(String[] args) {
        //（1）栈帧str1  直接指向  存在于在字符串常量池中的“Hello”
        String str1 = "Hello";
        System.out.println(str1);
        //(2)
        //检查字符串常量池是否有"Hello"字符串的引用
        // 如果有就直接给value[]数组的引用
        //如果没有，那么将"Hello"存于字符串常量池，同时将存进去的将引用赋值给value[]数组
        String str2 = new String("Hello");
        System.out.println(str2);
        // (3)
        //public String(char value[]) {this.value = Arrays.copyOf(value)}
        //栈上： value
        //堆上：  'H','e','l','l','o'
        // str3  new String(value)   Arrays.copyOf(value)

        char[] value = {'H','e','l','l','o'};
        String str3 = new String(value);
        System.out.println(str3);
    }

}
